import {BaseModel} from './baseModel';

export interface User {
  userName: string; //primary key
  displayName: string;
  parentUserName?: string; // card parent user
  passkeyName: string;
  userType: string; // common | card
  solanaAddress: string;
  latestChainId: string; // solana: solana-mainnet | solav-devnet, evm: hex chain id
  principalId: string;
  publicKey: string;
  createTime: number;
  modifyTime: number;
  expiration: number;
  isSelected: number; // 0 | 1
}

export interface UserWithCardNumber extends User {
  cardNumber?: string;
}

export class UserModel extends BaseModel {
  async createTable(): Promise<void> {
    try {
      const db = await BaseModel.getDB();
      const query = `
                CREATE TABLE IF NOT EXISTS user (
                    user_name TEXT NOT NULL PRIMARY KEY,
                    display_name TEXT NOT NULL,
                    parent_user_name TEXT,
                    passkey_name TEXT NOT NULL,
                    user_type TEXT NOT NULL,
                    solana_address TEXT NOT NULL,
                    latest_chain_id TEXT NOT NULL,
                    principal_id TEXT NOT NULL,
                    public_key TEXT NOT NULL,
                    create_time INTEGER NOT NULL,
                    modify_time INTEGER NOT NULL,
                    expiration INTEGER NOT NULL DEFAULT 0,
                    is_selected INTEGER NOT NULL DEFAULT 0
                );
            `;
      await db.executeSql(query);
    } catch (error) {
      console.log('create table error', error);
    }
  }

  async dropTable(): Promise<void> {
    const db = await BaseModel.getDB();
    await db.executeSql('DROP TABLE IF EXISTS user;');
  }

  async insert(user: User): Promise<number> {
    const db = await BaseModel.getDB();

    if (!user.parentUserName) {
      const query =
        'INSERT INTO user (user_name, display_name, passkey_name, user_type, solana_address, latest_chain_id, principal_id, public_key, create_time, modify_time, expiration, is_selected) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
      const result = await db.executeSql(query, [
        user.userName,
        user.displayName,
        user.passkeyName,
        user.userType,
        user.solanaAddress,
        user.latestChainId,
        user.principalId,
        user.publicKey,
        user.createTime,
        user.modifyTime,
        user.expiration,
        user.isSelected,
      ]);
      return result[0].insertId;
    } else {
      const query =
        'INSERT INTO user (user_name, display_name, parent_user_name, passkey_name, user_type, solana_address, latest_chain_id, principal_id, public_key, create_time, modify_time, expiration, is_selected) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
      const result = await db.executeSql(query, [
        user.userName,
        user.displayName,
        user.parentUserName,
        user.passkeyName,
        user.userType,
        user.solanaAddress,
        user.latestChainId,
        user.principalId,
        user.publicKey,
        user.createTime,
        user.modifyTime,
        user.expiration,
        user.isSelected,
      ]);
      return result[0].insertId;
    }
  }

  async update(user: User): Promise<void> {
    const db = await BaseModel.getDB();
    const query =
      'UPDATE user set display_name = ?, parent_user_name = ?, passkey_name = ?, user_type = ?, solana_address = ?, latest_chain_id = ?, principal_id = ?, create_time = ?, modify_time = ?, expiration = ?, is_selected = ? WHERE user_name = ?';
    await db.executeSql(query, [
      user.displayName,
      user.parentUserName ? user.parentUserName : null,
      user.passkeyName,
      user.userType,
      user.solanaAddress,
      user.latestChainId,
      user.principalId,
      user.createTime,
      user.modifyTime,
      user.expiration,
      user.isSelected,
      user.userName,
    ]);
  }

  async getByUserName(userName: string): Promise<User | null> {
    const db = await BaseModel.getDB();
    const query =
      'SELECT user_name as userName, parent_user_name as parentUserName, display_name as displayName, passkey_name as passkeyName, user_type as userType, solana_address as solanaAddress, latest_chain_id as latestChainId, principal_id as principalId, create_time as createTime, modify_time as modifyTime, expiration, is_selected as isSelected FROM user WHERE user_name = ?';
    const [results] = await db.executeSql(query, [userName]);
    const users = results.rows.raw() as User[];
    return users.length > 0 ? users[0] : null;
  }

  async save(user: User): Promise<void> {
    try {
      const tmpUser = await this.getByUserName(user.userName);
      console.log('tmpUser', tmpUser);
      if (tmpUser != null) {
        await this.update(user);
      } else {
        await this.insert(user);
      }
    } catch (error) {
      console.log(error);
    }
  }

  async getAll(): Promise<User[]> {
    const db = await BaseModel.getDB();
    const query =
      'SELECT user_name as userName, parent_user_name as parentUserName, display_name as displayName, passkey_name as passkeyName, user_type as userType, solana_address as solanaAddress, latest_chain_id as latestChainId, principal_id as principalId, create_time as createTime, modify_time as modifyTime, expiration, is_selected as isSelected FROM user';
    const [results] = await db.executeSql(query);
    const users = results.rows.raw() as User[];

    return users;
  }

  async getTotalNumber(): Promise<Number> {
    const db = await BaseModel.getDB();
    const query = 'select count(user_name) as total from user';
    const [results] = await db.executeSql(query);
    const result = results.rows.raw()[0];

    return result['total'];
  }

  async getAllWithCardNumber(): Promise<UserWithCardNumber[]> {
    const db = await BaseModel.getDB();
    const query =
      'select user.user_name as userName, user.parent_user_name as parentUserName, user.display_name as displayName, user.passkey_name as passkeyName, user.user_type as userType, user.solana_address as solanaAddress, user.latest_chain_id as latestChainId, user.principal_id as principalId, user.create_time as createTime, user.modify_time as modifyTime, user.expiration, user.is_selected as isSelected, card.card_number as cardNumber from user left join card on user.user_name = card.user_name';
    const [results] = await db.executeSql(query);
    const users = results.rows.raw() as UserWithCardNumber[];

    return users;
  }
}
